<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务，帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2024 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件，不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\api\service\sharp;

use think\model\Collection;
use app\api\model\Goods as GoodsModel;
use app\api\model\sharp\Active as ActiveModel;
use app\api\model\sharp\Goods as SharpGoodsModel;
use app\api\model\sharp\ActiveTime as ActiveTimeModel;
use app\api\model\sharp\GoodsSku as SharpGoodsSkuModel;
use app\api\model\sharp\ActiveGoods as ActiveGoodsModel;
use app\common\enum\sharp\GoodsStatus as GoodsStatusEnum;
use app\common\enum\sharp\ActiveStatus as ActiveStatusEnum;
use app\common\service\BaseService;
use app\common\library\helper;
use cores\exception\BaseException;

/**
 * 秒杀活动服务类
 * Class Active
 * @package app\api\service\sharp
 */
class Active extends BaseService
{
    // 活动会场模型
    private ActiveModel $ActiveModel;

    // 活动会场场次模型
    private ActiveTimeModel $ActiveTimeModel;

    /**
     * 构造方法
     * Active constructor.
     */
    public function initialize()
    {
        $this->ActiveModel = new ActiveModel;
        $this->ActiveTimeModel = new ActiveTimeModel;
    }

    /**
     * 获取秒杀活动会场首页数据
     * @return array
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getHallHome(): array
    {
        // 获取秒杀首页顶部菜单
        $tabbar = $this->getActiveTabbar();
        empty($tabbar) && throwError('很抱歉，暂无秒杀活动');
        // 获取活动商品
        $goodsList = $this->getGoodsListByActiveTimeId($tabbar[0]['active_time_id']);
        return compact('tabbar', 'goodsList');
    }

    /**
     * 获取秒杀活动组件数据
     * @param array $goodsParm 商品查询参数
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getSharpModular(array $goodsParm = []): array
    {
        // 获取秒杀活动列表
        $tabbar = $this->getActiveTabbar();
        if (empty($tabbar)) {
            return ['active' => null, 'goodsList' => []];
        }
        return [
            // 秒杀活动
            'active' => $tabbar[0],
            // 活动商品列表
            'goodsList' => $this->getGoodsListByActiveTimeId($tabbar[0]['active_time_id'], $goodsParm),
        ];
    }

    /**
     * 根据活动场次ID获取商品列表
     * @param int $activeTimeId 秒杀会场场次ID
     * @param array $goodsParm 商品查询参数
     * @return Collection|\think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getGoodsListByActiveTimeId(int $activeTimeId, array $goodsParm = [])
    {
        // 设置默认查询参数
        $model = new ActiveGoodsModel;
        $goodsParm = $model->setQueryDefaultValue($goodsParm, [
            'status' => 1   // 商品状态: 上架
        ]);
        // 列表数据
        $list = ActiveGoodsModel::getGoodsListByActiveTimeId($activeTimeId, $goodsParm);
        // 隐藏冗余的属性
        return $list->hidden(SharpGoodsModel::getHidden(['goods']));
    }

    /**
     * 获取活动商品详情
     * @param int $activeTimeId 秒杀会场场次ID
     * @param int $sharpGoodsId 秒杀商品ID
     * @return array
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getActiveGoodsDetail(int $activeTimeId, int $sharpGoodsId): array
    {
        // 活动详情
        $active = $this->getGoodsActive($activeTimeId, $sharpGoodsId);
        // 商品详情
        $model = new ActiveGoodsModel;
        $goods = $model->getGoodsActiveDetail($active, $sharpGoodsId, true);
        return compact('active', 'goods');
    }

    /**
     * 获取订单提交的商品列表
     * @param int $activeTimeId 秒杀会场场次ID
     * @param int $sharpGoodsId 秒杀商品ID
     * @param string $goodsSkuId 商品skuID
     * @param int $goodsNum 购买数量
     * @return array
     * @throws \cores\exception\BaseException
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getCheckoutGoodsList(int $activeTimeId, int $sharpGoodsId, string $goodsSkuId, int $goodsNum): array
    {
        // 验证秒杀活动状态
        $this->checkGoodsActiveStatus($activeTimeId, $sharpGoodsId);
        // 秒杀商品详情
        $model = new ActiveGoodsModel;
        $sharpGoods = $model->getGoodsBasic($sharpGoodsId, true);
        // 商品sku信息
        $sharpGoods['skuInfo'] = SharpGoodsSkuModel::getSkuInfo($sharpGoods['goods_id'], $sharpGoodsId, $goodsSkuId);
        // 商品封面 (优先sku封面)
        $sharpGoods['goods_image'] = $sharpGoods['skuInfo']['goods_image'] ?: $sharpGoods['goods_image'];
        // 商品列表 (隐藏用不到的属性)
        $goodsList = [$sharpGoods->hidden(GoodsModel::getHidden(['content', 'images', 'goods_images']))];
        foreach ($goodsList as &$item) {
            // 商品价格
            $item['goods_price'] = $item['skuInfo']['seckill_price'];
            $item['line_price'] = $item['original_price'];
            // 记录秒杀商品ID
            $item['goods_sku_id'] = $item['skuInfo']['goods_sku_id'];
            $item['goods_source_id'] = $item['sharp_goods_id'];
            // 商品购买数量
            $item['total_num'] = $goodsNum;
            // 商品购买总金额
            $item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum);
        }
        return $goodsList;
    }

    /**
     * 活动详情
     * @param int $activeTimeId 秒杀会场场次ID
     * @param int $sharpGoodsId 秒杀商品ID
     * @return array
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getGoodsActive(int $activeTimeId, int $sharpGoodsId): array
    {
        // 获取活动商品的关联信息
        $model = $this->getActiveGoods($activeTimeId, $sharpGoodsId);
        if (empty($model) || !($model['active']['status'] && $model['active_time']['status'])) {
            throwError('很抱歉，该活动不存在或已结束');
        }
        // 开始/截止时间
        $startTime = $model['active']['active_date'] + ($model['active_time']->getData('active_time') * 60 * 60);
        $endTime = $startTime + (1 * 60 * 60);
        // 活动商品状态
        $activeStatus = $this->getActivcGoodsStatus($startTime, $endTime);
        // 整理活动数据
        return [
            'active_id' => $model['active_id'],
            'active_time_id' => $model['active_time_id'],
            'active_time' => $model['active_time']['active_time'],
            'sales_actual' => $model['sales_actual'],
            'start_time' => $this->onFormatTime($startTime),
            'end_time' => $this->onFormatTime($endTime),
            'active_status' => $activeStatus,
            'count_down_time' => $this->getGoodsActiveCountDownTime($activeStatus, $startTime, $endTime),
            'store_id' => $model['store_id'],
        ];
    }

    /**
     * 验证秒杀活动状态
     * @param int $activeTimeId 秒杀会场场次ID
     * @param int $sharpGoodsId 秒杀商品ID
     * @return void
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function checkGoodsActiveStatus(int $activeTimeId, int $sharpGoodsId)
    {
        // 获取活动详情
        $active = $this->getGoodsActive($activeTimeId, $sharpGoodsId);
        if ($active['active_status'] != ActiveStatusEnum::STATE_BEGIN) {
            throwError('很抱歉，该活动未开始');
        }
    }

    /**
     * 获取活动商品的关联信息
     * @param int $activeTimeId 秒杀会场场次ID
     * @param int $sharpGoodsId 秒杀商品ID
     * @return array|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getActiveGoods(int $activeTimeId, int $sharpGoodsId)
    {
        static $data = [];
        $index = "{$activeTimeId}_{$sharpGoodsId}";
        if (!isset($data[$index])) {
            $model = ActiveGoodsModel::getGoodsActive($activeTimeId, $sharpGoodsId);
            !empty($model) && $data[$index] = $model;
        }
        return $data[$index];
    }

    /**
     * 活动商品倒计时
     * @param int $activeStatus 活动状态
     * @param int $startTime 开始时间
     * @param int $endTime 截止时间
     * @return false|string
     */
    private function getGoodsActiveCountDownTime(int $activeStatus, int $startTime, int $endTime)
    {
        // 活动已开始：结束时间
        if ($activeStatus == GoodsStatusEnum::STATE_BEGIN) {
            return $this->onFormatTime($endTime);
        }
        // 活动未开始: 开始时间
        if ($activeStatus == GoodsStatusEnum::STATE_SOON) {
            return $this->onFormatTime($startTime);
        }
        return false;
    }

    /**
     * 活动商品状态
     * @param int $startTime 开始时间
     * @param int $endTime 截止时间
     * @return int
     */
    private function getActivcGoodsStatus(int $startTime, int $endTime): int
    {
        $nowTime = time();
        if ($nowTime < $startTime) {
            return GoodsStatusEnum::STATE_SOON;
        }
        if ($nowTime < $endTime) {
            return GoodsStatusEnum::STATE_BEGIN;
        }
        return GoodsStatusEnum::STATE_END;
    }

    /**
     * 获取秒杀首页顶部菜单
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getActiveTabbar(): array
    {
        // 当天的活动
        $todyActive = $this->ActiveModel->getNowActive();
        $data = [];
        if (!empty($todyActive)) {
            // 当前进行中的活动
            $data[] = $this->getBeginActive($todyActive);
            // 获取即将开始的活动
            $data = array_merge($data, $this->getSoonActive($todyActive));
        }
        // 获取预告的活动
        $data[] = $this->getNoticeActive();
        return array_values(array_filter($data));
    }

    /**
     * 获取当前进行中的活动
     * @param $todyActive
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getBeginActive($todyActive): array
    {
        // 当前的时间点
        $model = $this->ActiveTimeModel->getNowActiveTime($todyActive['active_id']);
        if (empty($model)) return [];
        // 整理数据
        $startTime = $todyActive['active_date'] + ($model->getData('active_time') * 60 * 60);
        $endTime = $startTime + (1 * 60 * 60);
        return [
            'active_id' => $todyActive['active_id'],
            'active_time_id' => $model['active_time_id'],
            'active_time' => $model['active_time'],
            'start_time' => $this->onFormatTime($startTime),
            'end_time' => $this->onFormatTime($endTime),
            'count_down_time' => $this->onFormatTime($endTime),
            'status' => ActiveStatusEnum::STATE_BEGIN,
            'status_text' => '已开抢',
            'status_text2' => '正在疯抢',
            'sharp_modular_text' => '正在疯抢',
        ];
    }

    /**
     * 获取即将开始的活动
     * @param $todyActive
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getSoonActive($todyActive): array
    {
        // 当前的时间点
        $list = $this->ActiveTimeModel->getNextActiveTimes($todyActive['active_id']);
        if (empty($list) || $list->isEmpty()) return [];
        // 整理数据
        $data = [];
        foreach ($list as $item) {
            $startTime = $todyActive['active_date'] + ($item->getData('active_time') * 60 * 60);
            $endTime = $startTime + (1 * 60 * 60);
            $data[] = [
                'active_id' => $todyActive['active_id'],
                'active_time_id' => $item['active_time_id'],
                'active_time' => $item['active_time'],
                'start_time' => $this->onFormatTime($startTime),
                'end_time' => $this->onFormatTime($endTime),
                'count_down_time' => $this->onFormatTime($startTime),
                'status' => ActiveStatusEnum::STATE_SOON,
                'status_text' => '即将开抢',
                'status_text2' => '即将开抢',
                'sharp_modular_text' => "{$item['active_time']} 场预告",
            ];
        }
        return $data;
    }

    /**
     * 获取预告的活动
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getNoticeActive(): array
    {
        // 下一场活动
        $nextActive = $this->ActiveModel->getNextActive();
        if (empty($nextActive)) return [];
        // 第一个时间点
        $model = $this->ActiveTimeModel->getRecentActiveTime($nextActive['active_id']);
        if (empty($model)) return [];
        // 整理数据
        $startTime = $nextActive['active_date'] + ($model->getData('active_time') * 60 * 60);
        $endTime = $startTime + (1 * 60 * 60);
        return [
            'active_id' => $nextActive['active_id'],
            'active_time_id' => $model['active_time_id'],
            'active_time' => $model['active_time'],
            'start_time' => $this->onFormatTime($startTime),
            'end_time' => $this->onFormatTime($endTime),
            'count_down_time' => $this->onFormatTime($startTime),
            'status' => ActiveStatusEnum::STATE_NOTICE,
            'status_text' => '预告',
            'status_text2' => $this->onFormatTime($startTime) . ' 开始',
            'sharp_modular_text' => $this->onFormatTime($startTime) . ' 开始',
        ];
    }

    /**
     * 将时间戳格式化为日期时间 (精确到分钟)
     * @param int $timeStamp 时间戳
     * @return false|string
     */
    private function onFormatTime(int $timeStamp)
    {
        return date('Y-m-d H:i', $timeStamp);
    }
}